/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved.
 * Description:
 * Author: g00421808
 * Create: 6/24/2022
 * Notes:
 */

#ifndef JSBIND_JSBIND_H
#define JSBIND_JSBIND_H

#include <string>

#include "jsbind/definer/function_definer.h"
#include "jsbind/definer/class_definer.h"
#include "jsbind/callback/napi/callback.h"

namespace Jsb {
class JSBIND_EXPORT JSBind {
public:
#if JSBIND_USING_NAPI
    static napi_value BindSymbols(napi_env env, napi_value exports);
#endif // JSBIND_USING_NAPI

    static int bindFunction(const std::string& name, JSFunction func);

#if JSBIND_SUPPORT_DECLARATION
    static void Reflect(Jsb::Callback<void (intptr_t, int32_t)> outputBuildInType,
                        Jsb::Callback<void (std::string, std::vector<intptr_t>)> outputFunction);
    static void QueryType(intptr_t typeId,
                          Jsb::Callback<void (int32_t, std::vector<intptr_t>)> outputType);
#endif

    static const JSFunction* GetJSFunction(const std::string& name);
private:
};

template <typename... Aliases>
const char *AliasName(const char *name, Aliases... aliases) {
    return(name);
}

template <typename... Aliases>
const char *AliasName(const char *name, const char *alias, Aliases... aliases) {
    return(alias);
}
} // namespace Jsb

#define JSBIND_CONCAT(a, b) a ## b

#define JSBIND_UNIQUE(name, line) JSBIND_CONCAT(name, line)

#define JSBIND_ALIAS(name, alias) alias

#define JSBIND_FUNCTION(__name, ...) Jsb::FunctionDefiner JSBIND_UNIQUE(definer, __LINE__)(Jsb::AliasName(#__name, ##__VA_ARGS__), &__name)

#define JSBIND_PFUNCTION(__name, ...) Jsb::PFunctionDefiner JSBIND_UNIQUE(definer, __LINE__)(Jsb::AliasName(#__name, ##__VA_ARGS__), &__name)

#define JSBIND_GLOBAL() namespace

#define JSBIND_CLASS(__class)                                                                                 \
	template<class C> struct ClassDefiner##__class {                                                \
		ClassDefiner##__class();                                                                            \
		Jsb::ClassDefiner<__class> definer;                                                                \
	};                                                                                                      \
	static struct ClassDefiner##__class<__class> classDefiner##__class;                                     \
	template<class C> ClassDefiner##__class<C>::ClassDefiner##__class():definer(#__class)

#define JSBIND_CONSTRUCTOR definer.AddConstructor

#define JSBIND_SETTER(__name, __setter) definer.AddSetter(__name, &C::__setter)

#define JSBIND_FIELD(__name, __getter, __setter) definer.AddField(__name, &C::__getter, &C::__setter)

#define JSBIND_METHOD(__method, ...) definer.AddMethod(Jsb::AliasName(#__method, ##__VA_ARGS__), &C::__method)

#define JSBIND_PMETHOD(__method, ...) definer.AddPMethod(Jsb::AliasName(#__method, ##__VA_ARGS__), &C::__method)


#define JSBIND_ADDON_LAZY(addonName)                                                                               \
  EXTERN_C_START                                                                                                \
  static napi_module _module = {                                                                                \
      .nm_version =1,                                                                                           \
      .nm_flags = 0,                                                                                            \
      .nm_filename = nullptr,                                                                                   \
      .nm_register_func = Jsb::JSBind::BindSymbols,                                                             \
      .nm_modname = #addonName,                                                                                 \
      .nm_priv = ((void*)0),                                                                                    \
      .reserved = { 0 },                                                                                        \
  };                                                                                                            \
  extern "C" __attribute__((constructor)) void Register##addonName(void) {                                      \
    napi_module_register(&_module);                                                                             \
    JSB_LOG(INFO) << "register jsbind addon: " << #addonName;                                                   \
  }    \
  EXTERN_C_END

#define JSBIND_ADDON(addonName)                                          \
  JSBIND_ADDON_LAZY(addonName)

#define JSBIND_ADDON_X(addonName, constructorAlias)                                                             \
  EXTERN_C_START                                                                                                \
  static napi_module _module = {                                                                                \
      .nm_version =1,                                                                                           \
      .nm_flags = 0,                                                                                            \
      .nm_filename = nullptr,                                                                                   \
      .nm_register_func = Jsb::JSBind::BindSymbols,                                                             \
      .nm_modname = #addonName,                                                                                 \
      .nm_priv = ((void*)0),                                                                                    \
      .reserved = { 0 },                                                                                        \
  };                                                                                                            \
  extern "C" __attribute__((constructor)) void Register##constructorAlias(void) {                               \
    napi_module_register(&_module);                                                                             \
    JSB_LOG(INFO) << "register jsbind addon: " << #addonName;                                                   \
  }    \
  EXTERN_C_END

#endif //JSBIND_JSBIND_H
